/* eslint-disable no-use-before-define */
/* eslint-disable no-cond-assign */
/* eslint-disable no-continue */
/* eslint-disable camelcase */
/**
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { ExifTransform } from './type';

export function useImageCropper() {
    /** 从数据中获取图片元信息 */
    function getTransformationsFromExifData(exifRotationOrBase64Image: number | string): ExifTransform {
        if (typeof exifRotationOrBase64Image === 'string') {
            exifRotationOrBase64Image = getExifRotation(exifRotationOrBase64Image);
        }
        switch (exifRotationOrBase64Image) {
        case 2: return { rotate: 0, flip: true };
        case 3: return { rotate: 2, flip: false };
        case 4: return { rotate: 2, flip: true };
        case 5: return { rotate: 1, flip: true };
        case 6: return { rotate: 1, flip: false };
        case 7: return { rotate: 3, flip: true };
        case 8: return { rotate: 3, flip: false };
        default: return { rotate: 0, flip: false };
        }
    }
    /** 获取图片元信息的旋转值 */
    function getExifRotation(imageBase64: string): number {
        const view = new DataView(base64ToArrayBuffer(imageBase64));
        if (view.getUint16(0, false) !== 0xFFD8) {
            return -2;
        }
        const length = view.byteLength;
        let offset = 2;
        while (offset < length) {
            if (view.getUint16(offset + 2, false) <= 8) return -1;
            const marker = view.getUint16(offset, false);
            offset += 2;
            if (marker === 0xFFE1) {
                if (view.getUint32(offset += 2, false) !== 0x45786966) {
                    return -1;
                }

                const little = view.getUint16(offset += 6, false) === 0x4949;
                offset += view.getUint32(offset + 4, little);
                const tags = view.getUint16(offset, little);
                offset += 2;
                for (let i = 0; i < tags; i++) {
                    if (view.getUint16(offset + (i * 12), little) === 0x0112) {
                        return view.getUint16(offset + (i * 12) + 8, little);
                    }
                }
            } else if ((marker & 0xFF00) !== 0xFF00) {
                break;
            } else {
                offset += view.getUint16(offset, false);
            }
        }
        return -1;
    }

    function base64ToArrayBuffer(imageBase64: string) {
        imageBase64 = imageBase64.replace(/^data:([^;]+);base64,/gmi, '');
        const binaryString = atob(imageBase64);
        const len = binaryString.length;
        const bytes = new Uint8Array(len);
        for (let i = 0; i < len; i++) {
            bytes[i] = binaryString.charCodeAt(i);
        }
        return bytes.buffer;
    }

    function resizeCanvas(canvas: HTMLCanvasElement, width: number, height: number) {
        const width_source = canvas.width;
        const height_source = canvas.height;
        width = Math.round(width);
        height = Math.round(height);

        const ratio_w = width_source / width;
        const ratio_h = height_source / height;
        const ratio_w_half = Math.ceil(ratio_w / 2);
        const ratio_h_half = Math.ceil(ratio_h / 2);

        const ctx = canvas.getContext('2d');
        if (ctx) {
            const img = ctx.getImageData(0, 0, width_source, height_source);
            const img2 = ctx.createImageData(width, height);
            const { data } = img;
            const data2 = img2.data;

            for (let j = 0; j < height; j++) {
                for (let i = 0; i < width; i++) {
                    const x2 = (i + j * width) * 4;
                    const center_y = j * ratio_h;
                    let weight = 0;
                    let weights = 0;
                    let weights_alpha = 0;
                    let gx_r = 0;
                    let gx_g = 0;
                    let gx_b = 0;
                    let gx_a = 0;

                    const xx_start = Math.floor(i * ratio_w);
                    const yy_start = Math.floor(j * ratio_h);
                    let xx_stop = Math.ceil((i + 1) * ratio_w);
                    let yy_stop = Math.ceil((j + 1) * ratio_h);
                    xx_stop = Math.min(xx_stop, width_source);
                    yy_stop = Math.min(yy_stop, height_source);

                    for (let yy = yy_start; yy < yy_stop; yy++) {
                        const dy = Math.abs(center_y - yy) / ratio_h_half;
                        const center_x = i * ratio_w;
                        const w0 = dy * dy; // pre-calc part of w
                        for (let xx = xx_start; xx < xx_stop; xx++) {
                            const dx = Math.abs(center_x - xx) / ratio_w_half;
                            const w = Math.sqrt(w0 + dx * dx);
                            if (w >= 1) {
                                continue;
                            }
                            // hermite filter
                            weight = 2 * w * w * w - 3 * w * w + 1;
                            const pos_x = 4 * (xx + yy * width_source);
                            // alpha
                            gx_a += weight * data[pos_x + 3];
                            weights_alpha += weight;
                            // colors
                            if (data[pos_x + 3] < 255) { weight = weight * data[pos_x + 3] / 250; }
                            gx_r += weight * data[pos_x];
                            gx_g += weight * data[pos_x + 1];
                            gx_b += weight * data[pos_x + 2];
                            weights += weight;
                        }
                    }
                    data2[x2] = gx_r / weights;
                    data2[x2 + 1] = gx_g / weights;
                    data2[x2 + 2] = gx_b / weights;
                    data2[x2 + 3] = gx_a / weights_alpha;
                }
            }

            canvas.width = width;
            canvas.height = height;

            // draw
            ctx.putImageData(img2, 0, 0);
        }
    }

    return {
        resizeCanvas,
        getTransformationsFromExifData,
    };
}
